/********************************************************************************************************************************
 * 《C ++ Primer》第五版中文版，读书笔记：按照书中的的示例源码和每一个小章节的课后习题，自己动手写代码实现，作为联系和测试。
 * Date: 2024-03-29 18:20
 * Author: dimon.chen 
 * EMali: 1181302388@qq.com 
 * 
 * 章节: 第三章 字符串，向量和数组
 *       3.2.3 处理string对象中的字符
 * 
 ********************************************************************************************************************************/


#include <iostream>
#include <string>
#include <cctype>               //使用C++标准库定义的C标准库头文件
                                //C++标准库头文件把 C语言标准库定义的头文件 name.h
                                //修改为 cname，即去掉结尾的.h和加上前缀c表示头文件来自C语言标准库
                                //两者之间的很重要不同的一点是：定义在C++标准库头文件中的名字都在
                                //命名空间 std中，C语言标准库头文件则不然。
                                //cctype是C++标准库定义的C语言标准库头文件，对应于 ctype.h 

int main(int argc, char *argv[])
{
    /// 3.2.3节练习
    /// 练习3.6: 编写一段程序，使用范围for语句将字符串内的所有字符用X代替
    ///

    std::string s("Hello world, i am learing C++ Primter Edition 5!");
    //范围for(range for)语句遍历string 对象中的每一个字符
    //范围for的形式
    // for(declarationi:expression)
    // 其中expression是一个对象，表示一个序列
    // declaration定义你了一个变量，用来访问序列中的基本元素。每次迭代时变量使用序列中的下一个元素
    // 初始化

    std::cout << "s: " << std::endl << s << std::endl;
    for( char &c : s)                       //修改string对象中的字符，循环控制变量需要定义为复合类型引用
                                            //变量c每次迭代时依次用绑定到string对象的下一个字符
    {
        c = toupper(c);
    }
    std::cout << "to upper is: " << std::endl << s << std::endl;


    std::cout << "s: " << std::endl << s << std::endl;
    for( auto &c : s)                       //修改string对象中的字符，循环控制变量需要定义为复合类型引用
                                            //变量c每次迭代时依次用绑定到string对象的下一个字符
    {
        if(isalpha(c))
            c = 'X';
    }
    std::cout << "to upper is: " << std::endl << s << std::endl;


    std::cout << "s: " << std::endl << s << std::endl;
    for( auto &c : s)                       //修改string对象中的字符，循环控制变量需要定义为复合类型引用
                                            //变量c每次迭代时依次用绑定到string对象的下一个字符
    {
        //if(isalpha(c))
            c = 'X';
    }
    std::cout << "to upper is: " << std::endl << s << std::endl;



    /// 3.2.3节练习
    /// 练习3.7: 就上一题完成的程序而言，如果将循环控制变量的类型修改为 char 将会发生什么？
    ///         估计一下结果，然后实际编程进行验证。
    ///

    // 范围for循环控制变量的类型修改为 char，则定义的变量每一次是string对象中字符的拷贝，
    // 修改这个新定义的变量c，并不会修改string 对象中字符的值


    /// 3.2.3节练习
    /// 练习3.8: 分别列用while和传统的for循环重写第一题的程序，你觉得那种形式更好哪？为什么？
    ///
    s = "Hello world, i am learing C++ Primter Edition 5!";
    std::cout << "to upper is: " << std::endl << s << std::endl;
    decltype(s.size()) index = 0;
    while(index < s.size()){
        //使用下标运算符([])，下标运算符返回string对象该位置字符的引用
        s[index] = 'X';
        index++;
    }
    std::cout << "to upper is: " << std::endl << s << std::endl;


    s = "Hello world, i am learing C++ Primter Edition 5!";
    std::cout << "to upper is: " << std::endl << s << std::endl;
    //对于string对象，无论何时都要检查下标的合法性，下标需要大于等于0而小于s.size（）
    for(std::string::size_type n = 0; n < s.size(); n++)
    {
        if(isalpha(s[n]))
            s[n] = 'x';
    }
    std::cout << "to upper is: " << std::endl << s << std::endl;



    /// 3.2.3节练习
    /// 练习3.9: 下面程序有和作用，它合法么？如果不合法，为什么？
    ///
    std::string s1;       //默认初始化，一个空string对象
                    //对一个空string执行下标运算符，其结果是未知的.
    std::cout << s1[0] << std::endl;

    //错误： s是一个空string，对于string对象，无论何时使用下标都要检查下标的合法性
    // 下标的值需要大于等于0而小于s.size()的值，不再其范围的下标，其结果是未知的
    // 以此推断，对空string执行 s[0] 其结果是未知的


    std::string s2 = "c";
    //修正，首先判断string是否为空
    if(!s2.empty())
        std::cout << s2[0] << std::endl;


    /// 3.2.3节练习
    /// 练习3.10: 编写一段程序，读入一个包含标点符号的字符串，将标点符号去除后输出字符串剩余部分
    ///
    std::string line;
    std::string result;
    while(getline(std::cin, line)){
        if(!line.empty()){

            std::cout << "you enter: " << std::endl << line << std::endl;

            for(auto c : line)
            {
                if(!ispunct(c))
                    result += c;
            }

            std::cout << "remove puncation: " << std::endl << result << std::endl;
            break;
        }
    }

    
    /// 3.2.3节练习
    /// 练习3.11: 下方面的范围for(range for)语句合法么？如果合法，c的类型是什么？
    ///

    using std::string;


    //使用关键字 auto 类型指示符让编译器推断变量的类型
    //当定义引用时, auto 保留顶层const (top-level const)
    //c的类型为 const char & ，指向常量的引用（常量引用）
    //合法
    const string s3 = "keep out";
    for (auto &c : s3){
        std::cout << c << std::endl;
    }


    return 0;
}